package contegor.scire.structure.queries;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import contegor.scire.core.Concept;
import contegor.scire.core.Connectable;
import contegor.scire.core.Container;
import contegor.scire.core.InfiniteSingletonList;
import contegor.scire.core.Link;
import contegor.scire.core.NeedsPrecedent;
import contegor.scire.core.ReturnPoint;
import contegor.scire.core.ReturnPointAdapter;
import contegor.scire.core.Task;
import contegor.scire.core.TaskId;
import contegor.scire.core.Version;
import contegor.scire.core.WithConsequences;
import contegor.scire.core.queries.GetLinksFromTargets;
import contegor.scire.core.queries.Query;

/**
 * {@link CircularQuery} that, with a set of {@link Link}s given by its {@link NeedsPrecedent} interface,
 * gets the {@link Link}s of their source {@link Concept}s. If the source of the given {@link Link}s are
 * not {@link Concept}s, they are excluded from the search.
 *
 * @version $Revision$
 * @author  Héctor Cirbián Pousa ($Author$)
 */
@Version("$Id$")
public class CircularGetLinksDownstream extends CircularQuery<Link> implements WithConsequences<Link, Link> {
  private static final long serialVersionUID = -6872668652697080024L;

  private List<? extends Link> precedentResults        = null;
  private List<Query<Link>>    consequences            = null;
  private List<Link>           results                 = Collections.synchronizedList(new ArrayList<Link>());

  private transient ReturnPoint<Link> consequencesReturnPoint = null;
  
  
  private ReturnPoint<Link> generateConsequencesReturnPoint() {
    return new ReturnPointAdapter<Link>() {
      public void manage(TaskId taskId, Link result) {
        results.add(result);
      }
      public void manage(TaskId taskId, Exception exception) {
        returnPoint.manage(taskId, exception);
      }
    };
  }

  @Override
  public void setPrecedent(Task<? extends Link> task, Collection<? extends Link> results) {
    this.precedentResults = new ArrayList<Link>(results);
  }

  @Override
  public void process(Container targetContainer) {
    if (this.precedentResults != null) {
      this.consequences = new ArrayList<Query<Link>>(1);
      List<Concept> filteredResults = new LinkedList<Concept>();
      for (Link link: this.precedentResults) {
        if (link.isVisibleTo(this.taskId.getTransactionId())) {
          Connectable aux = link.getSource();
          // We need to check if the source is visible to this transaction.
          // Also if it is a Container. If is, we will get its children, checking also their visibility.
          if ((aux instanceof Concept) && (aux.isVisibleTo(this.taskId.getTransactionId()))) {
            if (aux instanceof Container) {
              for (Concept child: ((Container)aux).getConcepts())
                if (child.isVisibleTo(this.taskId.getTransactionId()))
                  filteredResults.add(child);
            }
            else
              filteredResults.add((Concept)aux);
          }
        }
      }
      if (filteredResults.size() > 0)
        this.consequences.add(new GetLinksFromTargets(taskId.next(true), this.getTargetContainerId(), filteredResults));
    }
  }

  @Override
  public List<? extends Task<Link>> getConsequences() {
    return this.consequences;
  }

  @Override
  public List<? extends ReturnPoint<? super Link>> getConsequencesReturnPoints() {
    if ((this.consequences == null) || (this.consequences.size() == 0))
      return Collections.emptyList();
    if (this.consequencesReturnPoint == null)
      this.consequencesReturnPoint = generateConsequencesReturnPoint();
    return new InfiniteSingletonList<ReturnPoint<? super Link>>(this.consequencesReturnPoint, consequences.size());
  }

  @Override
  public List<? extends Collection<Link>> getResultsForSubsequentTasks() {
    if ((this.subsequents == null) || (this.subsequents.size() == 0))
      return Collections.emptyList();
    return new InfiniteSingletonList<List<Link>>(this.results, this.subsequents.size());
  }

  @Override
  public List<? extends ReturnPoint<? super Link>> getSubsequentTasksReturnPoints() {
    if ((this.subsequents == null) || (this.subsequents.size() == 0))
      return Collections.emptyList();
    return new InfiniteSingletonList<ReturnPoint<? super Link>>(this.returnPoint, subsequents.size());
  }

  @Override
  public CircularGetLinksDownstream withIt(NeedsPrecedent<? super Link, Link> subsequent) {
    super.withIt(subsequent);
    return this;
  }
  
}
